package com.gdhz.xiaohengqin.reservation.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gdhz.xiaohengqin.reservation.constants.TeleOneConstant;
import com.gdhz.xiaohengqin.reservation.exception.TeleOneException;
import com.gdhz.xiaohengqin.reservation.model.CheckResponse;
import com.gdhz.xiaohengqin.reservation.model.ReservationRecordResponse;
import com.gdhz.xiaohengqin.reservation.model.entity.ReservationRecord;
import com.gdhz.xiaohengqin.reservation.repository.ReservationRecordRepository;
import com.gdhz.xiaohengqin.reservation.service.ReservationRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Swallows
 * @date 2025-05-29 09:47
 */
@Slf4j
@Service
public class ReservationRecordServiceImpl implements ReservationRecordService {


    private final WebClient webClient;
    private final ReservationRecordRepository reservationRecordRepository;
    private final ObjectMapper objectMapper;

    public ReservationRecordServiceImpl(WebClient.Builder builder,
                                        ReservationRecordRepository reservationRecordRepository,
                                        ObjectMapper objectMapper) {
        this.webClient = builder
//                .baseUrl(TeleOneConstant.SERVER_SIT_URL)
                .baseUrl(TeleOneConstant.SERVER_PROD_URL)
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .build();
        this.reservationRecordRepository = reservationRecordRepository;
        this.objectMapper = objectMapper;
    }

    @Override
    public Mono<ReservationRecordResponse> verifyByQrCode(String qrCode, String token) {

        Map<String, Object> params = new HashMap<>();
        params.put("url", qrCode);
        params.put(TeleOneConstant.TOKEN, token);
//        params.put(TeleOneConstant.TOKEN, TeleOneConstant.TOKEN_PROD);

        return postAndHandle(TeleOneConstant.QRCODE_VERIFY_URL, params, ReservationRecordResponse.class);
    }

    @Override
    public Mono<CheckResponse> verifyById(String id) {

        Map<String, Object> params = new HashMap<>();
        params.put("id", id);
        params.put(TeleOneConstant.TOKEN, TeleOneConstant.XIAO_HENG_QING_TOKEN_PROD);

        return postAndHandle(TeleOneConstant.CHECK_VISIT_URL, params, CheckResponse.class);
    }

    @Override
    public Mono<Boolean> hasExist(String url) {

        return Mono.just(ObjectUtils.isEmpty(reservationRecordRepository.findByUrl(url)));
    }

    @Override
    public Mono<Boolean> saveOrUpdate(ReservationRecord reservationRecord) {

        return reservationRecordRepository.save(reservationRecord)
                .flatMap(record -> Mono.just(ObjectUtils.isEmpty(record)));
    }

    private <T> Mono<T> postAndHandle(String uri, Object body, Class<T> targetType) {
        return webClient.post()
                .uri(uri)
                .bodyValue(body)
                .exchangeToMono(resp ->
                        resp.bodyToMono(String.class)
                                .defaultIfEmpty("")
                                .flatMap(raw -> {
                                    int code = resp.statusCode().value();
                                    log.info("智慧规建原始响应数据 [{} {}]: {}", code, uri, raw);

                                    if (resp.statusCode().is2xxSuccessful()) {
                                        try {
                                            return Mono.just(objectMapper.readValue(raw, targetType));
                                        } catch (JsonProcessingException e) {
                                            log.error("来自智慧规建的 JSON 解析失败 [{} {} {}]", code, uri, raw, e);
                                            return Mono.error(e);
                                        }
                                    }
                                    return Mono.error(new TeleOneException(code, raw));
                                })
                );
    }

}
